package org.wevil.util;

import org.apache.commons.io.FileUtils;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.nio.charset.Charset;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 文件操作工具类
 *
 * @author wevil
 * @since 2020/2/17
 */
public class FileUtil {

    /**
     * 全局文件目录集合
     */
    private static List<String> fileList = new ArrayList<>();

    // ============================ 查询 =======================================================//

    /**
     * 判断文件的结束符
     *
     * @return LF，CRLF，未检测到行结束符
     * */
    public static String detectLineEnding(String filePath) {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            int b;
            boolean foundCR = false;
            while ((b = fis.read()) != -1) {
                if (b == '\r') {
                    foundCR = true;
                } else if (b == '\n') {
                    if (foundCR) {
                        return "CRLF";
                    } else {
                        return "LF";
                    }
                } else {
                    foundCR = false;
                }
            }
        } catch (IOException ignored) {
        }
        return "未检测到行结束符";
    }

    /**
     * 获取一个目录下的所有文件的绝对路径
     * <p>默认读取在指定目录根目录下的.gitignore文件来忽略目录或文件</p>
     *
     * @param path 目录路径，例如： D:/tmp/file
     */
    public static List<String> getAllFilesAbsolutePath(String path) {
        List<String> ignorePatterns = loadGitIgnore(path);
        return getAllFilesAbsolutePath(path, ignorePatterns);
    }

    /**
     * 获取一个目录下的所有文件的绝对路径
     *
     * @param path 目录路径，例如： D:/tmp/file
     * @param ignorePatterns 忽略文件正则表达式
     */
    public static List<String> getAllFilesAbsolutePath(String path, List<String> ignorePatterns) {
        List<String> filePaths = new ArrayList<>();
        File directory = new File(path);
        if (!directory.exists() ||!directory.isDirectory()) {
            return filePaths;
        }
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    filePaths.addAll(getAllFilesAbsolutePath(file.getAbsolutePath(), ignorePatterns));
                } else {
                    String fileAbsolutePath = file.getAbsolutePath().replace("\\", "/");
                    if (shouldIgnore(fileAbsolutePath, ignorePatterns)) {
                        continue;
                    }
                    filePaths.add(fileAbsolutePath);
                }
            }
        }
        return filePaths;
    }

    /**
     * 获取一个目录下的所有文件
     *
     * @param path 文件路径，例如： D:/tmp/file
     */
    public static List<Map<String, Object>> getDirContent(String path) {
        List<Map<String, Object>> fileVoList = new ArrayList<>();
        File dir = new File(path);
        if (dir.isDirectory()) {
            File[] listFiles = dir.listFiles();
            if (listFiles != null) {
                for (File file : listFiles) {
                    Calendar cal = Calendar.getInstance();
                    long time = file.lastModified();
                    cal.setTimeInMillis(time);
                    Map<String, Object> map = new HashMap<>();
                    map.put("fileName", file.getName());
                    map.put("updateTime", cal.getTime());
                    if (file.isDirectory()) {
                        map.put("fileSize", org.apache.commons.io.FileUtils.sizeOfDirectory(file));
                        map.put("fileType", "dir");
                    } else {
                        map.put("fileSize", file.length());
                        map.put("fileType", getFileNameSuffix(file.getName()));
                    }
                    fileVoList.add(map);
                }
            }
        }
        return fileVoList;
    }

    /**
     * 递归遍历一个目录下的所有文件名
     *
     * @param path 文件路径
     * @return 返回所有文件路径集合，不包含目录
     */
    public static List<String> recursiveFileList(String path) {
        fileList.clear();
        recursiveFile(path);
        return fileList;
    }

    /**
     * 获取文件夹内所有文件 按照时间降序
     * @param path 文件目录
     * @param filenameFilter 文件过滤器
     * @return
     */
    public static List<File> getDirAllFileSortByTime(String path,FilenameFilter filenameFilter) {
        File dir = new File(path);
        List<File> files = new ArrayList<>();
        if (dir.isDirectory()) {
            File[] listFiles = dir.listFiles(filenameFilter);
            if (listFiles != null) {
                for(File file : listFiles){
                    if(!file.isDirectory()){
                        files.add(file);
                    }
                }
            }
        }
        if (!CollectionUtils.isEmpty(files)){
            files.sort((o1, o2) -> (int) (o2.lastModified() - o1.lastModified()));
        }
        return files;
    }

    /**
     * 按行读取，返回List
     *
     * @author wanglei
     * @since 2021/6/25
     * */
    public static List<String> readLine(File file){
        try {
            return FileUtils.readLines(file, Charset.defaultCharset());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

    /**
     * 读取文件内容，作为字符串返回
     */
    public static String readFileAsString(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new FileNotFoundException(filePath);
        }

        if (file.length() > 1024 * 1024 * 1024) {
            throw new IOException("File is too large");
        }

        StringBuilder sb = new StringBuilder((int) (file.length()));
        // 创建字节输入流
        FileInputStream fis = new FileInputStream(filePath);
        // 创建一个长度为10240的Buffer
        byte[] bbuf = new byte[10240];
        // 用于保存实际读取的字节数
        int hasRead = 0;
        while ( (hasRead = fis.read(bbuf)) > 0 ) {
            sb.append(new String(bbuf, 0, hasRead));
        }
        fis.close();
        return sb.toString();
    }

    /**
     * 获取文件的字节数组
     *
     * @param filePath 文件路径
     * @return 返回文件对应的字节数组
     */
    public static byte[] getFileBytes(String filePath) {
        try {
            BufferedInputStream in = new BufferedInputStream(new FileInputStream(filePath));
            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
            byte[] temp = new byte[1024];
            int size;
            while ((size = in.read(temp)) != -1) {
                out.write(temp, 0, size);
            }
            in.close();
            return out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取文件修改时间
     *
     * @param filePath 文件绝对路径
     * @return 返回文件修改时间
     */
    public static Timestamp getFileUpdateTime(String filePath) {
        File f = new File(filePath);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(f.lastModified());
        String str = sdf.format(cal.getTime());
        return Timestamp.valueOf(str);
    }

    /**
     * 判断文件是否存在
     *
     * @param path 文件绝对路径
     * @return true:存在，false:不存在
     */
    public static boolean isFileExists(String path) {
        File file = new File(path);
        return file.exists();
    }

    /**
     * 获取一个文件绝对路径中的路径部分
     *
     * @param fileAbsolutePath 文件绝对路径，比如：D:\file\test.txt
     * @return 返回文件的父目录，比如：D:\file
     */
    public static String getParentPath(String fileAbsolutePath) {
        File f = new File(fileAbsolutePath);
        return f.getParent();
    }

    /**
     * 获取一个文件绝对路径中的文件名
     *
     * @param fileAbsolutePath 文件绝对路径，比如：D:\file\test.txt
     * @return 返回文件的文件名，比如：test.txt
     */
    public static String getFileName(String fileAbsolutePath) {
        File f = new File(fileAbsolutePath);
        return f.getName();
    }

    /**
     * 检查一个目录是否是空文件夹
     *
     * @param path 文件目录
     * @return true：是空文件夹，false：不是空文件夹
     */
    public static boolean checkEmptyFolder(String path) {
        File f = new File(path);
        File[] file_list = f.listFiles();
        if (file_list == null || file_list.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * 获取两个目录中不同的文件名
     *
     * @param path1 目录1
     * @param path2 目录2
     * @return 返回两个目录里不同的文件名
     */
    public static List<String> getDifferentFrom2Path(String path1, String path2) {
        File[] file_array1 = new File(path1).listFiles();
        File[] file_array2 = new File(path2).listFiles();
        List<String> fileList1 = new ArrayList<>();
        List<String> fileList2 = new ArrayList<>();
        assert file_array1 != null;
        for (File f : file_array1) {
            fileList1.add(f.getName());
        }
        assert file_array2 != null;
        for (File f : file_array2) {
            fileList2.add(f.getName());
        }
        return ListUtil.get2ListDifferent(fileList1, fileList2);
    }

    /**
     * 获取文件名后缀，如果没有后缀，返回空
     *
     * @param fileName 文件名
     * @return 返回文件后缀名
     * @author wevil
     * @since 2020/2/17
     */
    public static String getFileNameSuffix(String fileName) {
        if (fileName.contains(".")) {
            return fileName.substring(fileName.lastIndexOf(".") + 1);
        }
        return "";
    }

    /**
     * 获取文件名不带后缀
     *
     * @param fileName 文件名
     * @return 返回文件名称，不带后缀名
     */
    public static String getFileNameNoSuffix(String fileName) {
        return fileName.substring(0, fileName.lastIndexOf("."));
    }

    /**
     * 获取File的内容
     *
     * @since 2022/2/27
     */
    public static String getFileContent(File file) {
        StringBuilder result = new StringBuilder();
        try {
            // 构造一个BufferedReader类来读取文件
            BufferedReader br = new BufferedReader(new FileReader(file));
            String s = null;
            // 使用readLine方法，一次读一行
            while ((s = br.readLine()) != null) {
                result.append(System.lineSeparator()).append(s);
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.toString();
    }

    /**
     * 获取文本文件的内容
     *
     * @param filePath 文件路径
     * @return 返回文件内容
     */
    public static String readTextContent(String filePath) {
        StringBuilder result = new StringBuilder();
        try {
            File file = new File(filePath);
            // 构造一个BufferedReader类来读取文件
            BufferedReader br = new BufferedReader(new FileReader(file));
            String s = null;
            // 使用readLine方法，一次读一行
            while ((s = br.readLine()) != null) {
                result.append(System.lineSeparator()).append(s);
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.toString();
    }

    // ============================ 删除 =======================================================//

    /**
     * 删除文件
     *
     * @param path 文件夹路径或文件路径
     * @author wanglei
     * @since 2020/2/2
     */
    public static boolean deleteFile(String path) {
        File file = new File(path);
        if (!file.exists()) {
            System.out.println("[删除文件] 没有要删除的文件！");
        }
        //是文件夹
        if (file.isDirectory()) {
            File[] listFile = file.listFiles();
            if (delAllFile(path)) {
                // 再删除最外层目录
                file.delete();
                System.out.println("[删除文件夹] 删除" + listFile.length + "个文件成功！");
                return true;
            } else {
                System.out.println("[删除文件夹] 删除文件夹失败！");
            }
            file.delete();
        }
        //是文件
        else {
            if (file.delete()) {
                System.out.println("[删除文件] 删除文件成功！");
                return true;
            } else {
                System.out.println("[删除文件] 删除文件失败！");
            }
        }
        return false;
    }


    // ============================ 转换 =======================================================//

    /**
     * 将字节数组转换为文件
     *
     * @param bfile    字节数组
     * @param filePath 文件所在目录
     * @param fileName 文件名
     */
    public static void writeFileByByteArray(byte[] bfile, String filePath, String fileName) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            File dir = new File(filePath);
            if (!dir.exists() && dir.isDirectory()) {// 判断文件目录是否存在
                dir.mkdirs();
            }
            file = new File(filePath + "/" + fileName);
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(bfile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    // ============================ 写入 =======================================================//

    /**
     * 文件写入内容
     *
     * @param filePath 要写入的文件绝对路径
     * @param str      要写入的字符串
     */
    public static void writeTxt2File(String filePath, String str) throws IOException {
        String path = getParentPath(filePath);
        // 判断有没有目录，没有就创建
        File path_temp = new File(path);
        if (!path_temp.exists()) {
            if (!path_temp.mkdirs()) {
                System.out.println("创建目录失败!");
                return;
            }
        }
        File file = new File(filePath);
        OutputStream out = new FileOutputStream(file);
        OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");
        osw.write(str);
        osw.flush();
        osw.close();
        out.close();
    }

    /**
     * 文件写入内容
     *
     * @author wanglei
     * @since 2021/6/25
     * */
    public static void writeTxt2File(File file, List<String> str){
        try {
            FileUtils.writeLines(file, str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件追加内容
     *
     * @param filePath 要写入的文件绝对路径
     * @param str      要追加的字符串
     * @author wanglei
     * @since 2020/12/11
     */
    public static void appendTxt2File(String filePath, String str) throws IOException {
        String path = getParentPath(filePath);
        // 判断有没有目录，没有就创建
        File path_temp = new File(path);
        if (!path_temp.exists()) {
            if (!path_temp.mkdirs()) {
                System.out.println("创建目录失败!");
                return;
            }
        }
        File file = new File(filePath);
        // 第二个参数表示追加内容
        Writer out = new FileWriter(file, true);
        out.write(str);
        out.close();
    }

    // ============================ 复制 =======================================================//

    /**
     * 复制单个文件
     *
     * @param oldPath 原文件绝对路径 如：c:/fqf.txt
     * @param newPath 新文件绝对路径 如：f:/fqf.txt
     * @return true：复制成功，false：复制失败
     */
    public static boolean copyFile(String oldPath, String newPath) {
        try {
            int byteread = 0;
            File oldfile = new File(oldPath);
            if (oldfile.exists()) { //文件存在时
                InputStream inStream = new FileInputStream(oldPath); //读入原文件
                FileOutputStream fs = new FileOutputStream(newPath);
                byte[] buffer = new byte[1444];
                while ((byteread = inStream.read(buffer)) != -1) {
                    fs.write(buffer, 0, byteread);
                }
                inStream.close();
                fs.close();
            }
            return true;
        } catch (Exception e) {
            System.out.println("复制单个文件操作出错");
            return false;
        }
    }

    /**
     * 批量复制文件
     *
     * @param oldFilePathList 原文件绝对路径集合
     * @param newPath         存放新文件的路径
     */
    public static void copyFile(List<String> oldFilePathList, String newPath) {
        for (String filePath : oldFilePathList) {
            File f = new File(filePath);
            String fileName = f.getName();
            // 创建文件夹
            File folder = new File(newPath);
            folder.mkdirs();
            copyFile(filePath, newPath + "\\" + fileName);
        }
    }

    // ============================ 修改 =======================================================//

    /**
     * LF文件转CRLF文件
     * */
    public static void convertLFToCRLF(String filePath) {
        File inputFile = new File(filePath);
        File tempFile = new File(filePath + ".tmp");

        try (BufferedReader reader = new BufferedReader(new FileReader(inputFile));
             BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) {

            String line;
            boolean isFirstLine = true;
            while ((line = reader.readLine()) != null) {
                if (!isFirstLine) {
                    writer.write("\r\n");
                }
                writer.write(line);
                isFirstLine = false;
            }
        } catch (IOException e) {
            System.err.println("文件处理时出现错误: " + e.getMessage());
            return;
        }

        if (inputFile.delete()) {
            if (!tempFile.renameTo(inputFile)) {
                System.err.println("无法将临时文件重命名为原文件。");
            }
        } else {
            System.err.println("无法删除原文件。");
        }
    }

    /**
     * 文件重命名
     *
     * @param path    文件所在目录
     * @param oldName 原文件名
     * @param newName 新文件名
     */
    public static void renameFile(String path, String oldName, String newName) {
        if (!oldName.equals(newName)) { //新的文件名和以前文件名不同时,才有必要进行重命名
            File oldFile = new File(path + "/" + oldName);
            File newFile = new File(path + "/" + newName);
            if (!oldFile.exists()) {
                return; //重命名文件不存在
            }
            if (newFile.exists()) { //若在该目录下已经有一个文件和新文件名相同，则不允许重命名
                System.out.println(newName + "已经存在！");
            } else {
                if (!oldFile.renameTo(newFile)) {
                    System.out.println("重命名失败");
                } else {
                    System.out.println("重命名 [" + oldName + "] 为 [" + newName + "]");
                }
            }
        } else {
            System.out.println("新文件名和旧文件名相同...");
        }
    }

    /**
     * 替换一个目路下所有的文件名中的字符串为另一个字符串
     *
     * @param rootPath 文件所在目录
     * @param oldStr   原字符串
     * @param newStr   新字符串
     */
    public static void replacePathAllName2NewName(String rootPath, String oldStr, String newStr) {
        // 递归出该目录下的所有文件和目录
        List<String> allFileList = FileUtil.recursiveQuery(rootPath, ".*");
        // for循环
        for (String filePath : allFileList) {
            // 找出目录和文件名
            String path = getParentPath(filePath);
            String oldFileName = getFileName(filePath); // 原来文件名
            String newFileName = oldFileName.replace(oldStr, newStr);
            if (!oldFileName.equals(newFileName)) {
                // 替换文件名
                renameFile(path, oldFileName, newFileName);
            }
        }
    }

    /**
     * 替换一个目路下所有目录和文件名中的部分名称为另一个名称, 传入List， 两个list大小要一样
     *
     * @param rootPath   文件所在目录
     * @param oldStrList 原字符串集合
     * @param newStrList 新字符串集合
     */
    public static void replacePathAllName2NewNameByList(String rootPath, List<String> oldStrList, List<String> newStrList) {
        // 递归出该目录下的所有文件和目录
        List<String> allFileList = FileUtil.recursiveQuery(rootPath, ".*");
        // for循环
        for (String filePath : allFileList) {
            // 找出目录和文件名
            String path = getParentPath(filePath);
            String oldFileName = getFileName(filePath); // 原来文件名
            String newFileName = oldFileName;
            // 遍历要替换的内容
            for (int i = 0; i < oldStrList.size(); i++) {
                newFileName = newFileName.replace(oldStrList.get(i), newStrList.get(i));
            }
            if (!oldFileName.equals(newFileName)) {
                // 替换文件名
                renameFile(path, oldFileName, newFileName);
            }
        }
    }

    /**
     * 通过正则表达式匹配替换文件内的内容。示例：会将@ApiModelProperty("用户id")替换为 [ 用户id ]
     * @param regex 整则表达式，示例；@ApiModelProperty\("(.*?)"\)
     * @param replace 替换的字符串，可以使用$1来替换匹配到的(.*?)内容，示例：[ $1 ]
     * */
    private static void replaceByRegex(String filePath, String regex, String replace) throws IOException {
        String content = readFileAsString(filePath);
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(content);
        String replaceContent = matcher.replaceAll(replace);
        writeTxt2File(filePath, replaceContent);
    }

    // ============================ 私有类 =======================================================//

    /**
     * 删除指定文件夹下所有文件
     *
     * @param path 文件夹完整绝对路径
     * @author wanglei
     * @since 2020/2/2
     */
    private static boolean delAllFile(String path) {
        boolean flag = false;
        File file = new File(path);
        if (!file.exists()) {
            return flag;
        }
        String[] tempList = file.list();
        File temp = null;
        for (String s : tempList) {
            if (path.endsWith(File.separator)) {
                temp = new File(path + s);
            } else {
                temp = new File(path + File.separator + s);
            }
            //是文件
            if (temp.isFile()) {
                temp.delete();
                flag = true;
            }
            //是文件夹
            if (temp.isDirectory()) {
                //先删除文件夹里面的文件
                delAllFile(path + "/" + s);
                //再删除空文件夹
                delFolder(path + "/" + s);
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 删除文件夹
     *
     * @param folderPath 文件夹完整绝对路径
     * @author wanglei
     * @since 2020/2/2
     */
    private static void delFolder(String folderPath) {
        try {
            //删除完里面所有内容
            delAllFile(folderPath);
            String filePath = folderPath;
            filePath = filePath.toString();
            File myFilePath = new File(filePath);
            //删除空文件夹
            myFilePath.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 递归遍历一个目录下的所有文件
     *
     * @param path 文件路径
     */
    private static void recursiveFile(String path) {
        File file = new File(path);
        // 1.判断文件
        if (file.exists()) {
            // 2.是文件该怎么执行
            if (file.isFile()) {
                fileList.add(file.getAbsolutePath());
            }
            // 3.获取文件夹路径下面的所有文件递归调用；
            if (file.isDirectory()) {
                File[] listFiles = file.listFiles();
                if (listFiles != null && listFiles.length > 0) {
                    for (File f : listFiles) {
                        recursiveFile(f.getAbsolutePath());
                    }
                }
            }
        }
    }

    /**
     * 递归查询一个目录里包含某个名称的
     *
     * @param path      文件路径
     * @param fileRegex 想要匹配的文件正则表达式
     */
    private static List<String> recursiveQuery(String path, String fileRegex) {
        List<String> fileList = new ArrayList<>();
        File file = new File(path);
        recursiveQuery(file, fileList, fileRegex);
        fileList.remove(path); // 删除rootPath
        return fileList;
    }

    /**
     * 递归查询一个目录里包含某个名称的
     *
     * @param file      文件对象
     * @param fileList  文件名集合，上层调用传一个空的集合，本层递归传递已经放入的文件名
     * @param fileRegex 文件名匹配的正则表达式
     */
    private static void recursiveQuery(File file, List<String> fileList, String fileRegex) {
        String fileName = file.getName();
        // 如果匹配到了
        if (Pattern.matches(fileRegex, fileName)) {
            fileList.add(file.getAbsolutePath());
        }
        // 如果是目录，递归查询
        if (file.isDirectory()) {
            File[] listFiles = file.listFiles();
            if (listFiles != null) {
                for (File listFile : listFiles) {
                    recursiveQuery(listFile, fileList, fileRegex);
                }
            }
        }
    }

    /**
     * 读取指定路径下的.gitignore文件，将表达式放在ignorePatterns
     * */
    private static List<String> loadGitIgnore(String directoryPath) {
        List<String> ignorePatterns = new ArrayList<>();
        File gitIgnoreFile = new File(directoryPath + File.separator + ".gitignore");
        if (gitIgnoreFile.exists()) {
            try (BufferedReader reader = new BufferedReader(new FileReader(gitIgnoreFile))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    if (!line.isEmpty() &&!line.startsWith("#")) {
                        String pattern = convertToRegex(line);
                        ignorePatterns.add(pattern);
                    }
                }
            } catch (IOException ignore) {
            }
        }
        return ignorePatterns;
    }

    /**
     * 将.gitignore里的表达式转换为正则表达式
     * */
    public static String convertToRegex(String gitignoreRule) {
        // 去除前后空格
        gitignoreRule = gitignoreRule.trim();

        // 如果是注释行，则返回空字符串
        if (gitignoreRule.startsWith("#")) {
            return "";
        }

        StringBuilder regex = new StringBuilder();

        // 开头加任意匹配，如果开头是**，则不加
        if(!gitignoreRule.startsWith("**")){
            regex.append(".*");
        }

        // 处理通配符
        for (int i = 0; i < gitignoreRule.length(); i++) {
            char c = gitignoreRule.charAt(i);
            switch (c) {
                case '*':
                    if (i + 1 < gitignoreRule.length() && gitignoreRule.charAt(i + 1) == '*') {
                        // 处理 ** 通配符，匹配任意数量的任意目录和文件
                        regex.append(".*");
                        i++;
                    } else {
                        // 处理 * 通配符，匹配任意数量的非路径分隔符字符
                        regex.append("[^/]*");
                    }
                    break;
                case '?':
                    // 处理 ? 通配符，匹配单个非路径分隔符字符
                    regex.append("[^/]");
                    break;
                case '.':
                    // 转义 . 字符
                    regex.append("\\.");
                    break;
                default:
                    regex.append(c);
            }
        }

        // 处理以 / 结尾的规则，要求匹配目录
        regex.append(".*");

        return regex.toString();
    }

    /**
     * 判断文件是否在忽略文件列表内
     * */
    private static boolean shouldIgnore(String path, List<String> ignorePatterns) {
        for (String pattern : ignorePatterns) {
            if (Pattern.matches(pattern, path)) {
                return true;
            }
        }
        return false;
    }
}
